#ifndef DELEGATEQTHREAD_H
#define DELEGATEQTHREAD_H

#include <QThread>
#include <mutex>
#include <condition_variable>
#include <QQueue>
#include <functional>

#include "QLibSsh_global.h"

class QLIBSSH_EXPORT DelegateQThread : public QThread {
    Q_OBJECT

public:
    explicit DelegateQThread(QObject *parent = nullptr)
        : QThread(parent)
    {
        mRunning = true;
        start();
    }

    ~DelegateQThread() override {
        {
            std::lock_guard locker(mMutex);
            mRunning = false;
            mCondition.notify_all();
        }
        wait();
    }

    template<class F, typename... Args>
    void Enqueue(F&& f, Args&& ... args) {
        std::lock_guard locker(mMutex);
        if (!mRunning) {
            return;
        }
        mTasks.enqueue(
            [f = std::forward<F>(f),
                args = std::make_tuple(std::forward<Args>(args)...)] {
                std::apply(f, args);
        });
        mCondition.notify_one();
    }

    template<class F>
    void Enqueue(F&& f) {
        std::lock_guard locker(mMutex);
        if (!mRunning) {
            return;
        }
        mTasks.enqueue([f = std::forward<F>(f)] (){f();});
        mCondition.notify_one();
    }

    void Stop() {
        std::lock_guard locker(mMutex);
        mRunning = false;
        mCondition.notify_all();
        qDebug() << "DelegateQThread::Stop()";
    }

protected:
    void run() override {
        while (mRunning) {
            std::function<void()> task;
            {
                std::unique_lock locker(mMutex);
                qDebug() << "DelegateQThread::run(), wait...";
                mCondition.wait(locker, [this] {
                    return !mRunning || !mTasks.empty();
                });
                if (!mTasks.empty()) {
                    task = mTasks.dequeue();
                }
            }
            if (task == nullptr) {
                qDebug() << "DelegateQThread::run(), task == nullptr";
                return;
            }
            task();
        }
        qDebug() << "DelegateQThread::run(), finished";
    }

private:
    std::mutex mMutex;
    std::condition_variable mCondition;
    QQueue<std::function<void()>> mTasks {};
    bool mRunning {false};
};


#endif // DELEGATEQTHREAD_H
